package com.altekis.kanbankaze.server;

import java.lang.reflect.Method;

import com.altekis.kanbankaze.shared.events.TrojanEvent;
import com.altekis.kanbankaze.shared.messages.Message;
import com.altekis.kanbankaze.shared.messages.TrojanMessage;
import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;

/**
 * Wrapper/utility for Channel API usage - includes message encoding + push
 * @author lordkhizir
 */
public class ChannelServer {
	private static final Method dummyMethod = getDummyMethod();

	private static ChannelService getChannelService() {
		return ChannelServiceFactory.getChannelService();
	}
	public static String createChannel(String userId) {
		String channelId = getChannelService().createChannel(userId); // TODO Use APP_KEY_PREFIX, or it's inherent?
		return channelId;
	}
	/**
	 * This method exists to make GWT RPC happy.
	 * <p>
	 * {@link RPC#encodeResponseForSuccess(java.lang.reflect.Method, Object)}
	 * insists that we pass it a Method that has a return type equal to the
	 * object we're encoding. What we really want to use is
	 * {@link RPC#encodeResponse(Class, Object, boolean, int, com.google.gwt.user.server.rpc.SerializationPolicy)},
	 * but it is unfortunately private.
	 */
	@SuppressWarnings("unused")
	private TrojanMessage dummyMethod(Message m, TrojanMessage tm) {
		throw new UnsupportedOperationException("This should never be called.");
	}

	private static Method getDummyMethod() {
		try {
			return ChannelServer.class.getDeclaredMethod("dummyMethod", Message.class, TrojanMessage.class);
		} catch (NoSuchMethodException e) {
			throw new RuntimeException("Unable to find the dummy RPC method.");
		}
	}

	private static String encodeMessage(Message msg) {
		try {
			return RPC.encodeResponseForSuccess(dummyMethod, (Message) msg);
		} catch (SerializationException e) {
			throw new RuntimeException("Unable to encode a message for push.\n" + msg, e);
		}
	}

	public static void sendMessage(String userId, Message message) {
		// Serialize message and send to client
		ChannelMessage channelMessage = new ChannelMessage(userId, encodeMessage(message));
		getChannelService().sendMessage(channelMessage);
	}
	
	public static void sendTrojanMessage(String userId, @SuppressWarnings("rawtypes") TrojanEvent event) {
		// Serialize message and send to client
		TrojanMessage message = new TrojanMessage(event);
		sendMessage(userId, message);
	}
}
